/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using NDODS.DAP;
using NDODS.DAP.Exceptions;
using NDODS.DAP.Primitives;

namespace NDODS.DAP.BaseTypes
{
    /// <summary> This class holds a one-dimensional array of OPeNDAP data types.
    /// It is the parent of both <code>DList</code> and <code>DArray</code>.
    /// This class uses a <code>PrimitiveVector</code> to hold the data and
    /// Deserialize it, thus allowing more efficient storage to be used for the
    /// primitive types.
    /// 
    /// </summary>
    /// <author>  jehamby
    /// </author>
    /// <version>  $Revision: 15901 $
    /// </version>
    /// <seealso cref="BaseType">
    /// </seealso>
    /// <seealso cref="DArray">
    /// </seealso>
    /// <seealso cref="PrimitiveVector">
    /// </seealso>
    [Serializable]
    public abstract class DVector : BaseType, IClientIO, ICloneable
    {
        /// <summary> Returns the OPeNDAP type name of the class instance as a <code>String</code>.
        /// 
        /// </summary>
        /// <returns> the OPeNDAP type name of the class instance as a <code>String</code>.
        /// </returns>
        public override String TypeName
        {
            get { return "Vector"; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the number of elements in the vector.
        /// 
        /// </summary>
        /// <returns> the number of elements in the vector.
        /// </returns>
        /// <summary> Sets the number of elements in the vector.  Allocates a new
        /// array of the desired size.  Note that if this is called multiple times,
        /// the old array and its contents will be lost!
        /// <p/>
        /// Only called inside of <code>Deserialize</code> method or in derived
        /// classes on server.
        /// 
        /// </summary>
        /// <param name="len">the number of elements in the array.
        /// </param>
        public virtual int Length
        {
            get
            {
                if (vals == null)
                    return 0;
                else
                    return vals.Length;
            }

            set { vals.Length = value; }
        }

        /// <summary> Returns the <code>PrimitiveVector</code> for this vector.  This can be
        /// cast to the appropriate type and used by a OPeNDAP client to Read or set
        /// individual values in the vector.
        /// 
        /// </summary>
        /// <returns> the attached <code>PrimitiveVector</code>.
        /// </returns>
        public virtual PrimitiveVector PrimitiveVector
        {
            get { return vals; }
        }

        /// <summary> The values in this <code>DVector</code>, stored in a
        /// <code>PrimitiveVector</code>.
        /// </summary>
        private PrimitiveVector vals;

        /// <summary> Constructs a new <code>DVector</code>.</summary>
        public DVector() : base()
        {
        }

        /// <summary> Constructs a new <code>DVector</code> with name <code>n</code>.
        /// 
        /// </summary>
        /// <param name="n">the name of the variable.
        /// </param>
        public DVector(String n) : base(n)
        {
        }

        /// <summary> Returns a clone of this <code>DVector</code>.  A deep copy is performed on
        /// all variables inside the <code>DVector</code>.
        /// 
        /// </summary>
        /// <returns> a clone of this <code>DVector</code>.
        /// </returns>
        public override Object Clone()
        {
            DVector v = (DVector) base.Clone();
            v.vals = (PrimitiveVector) vals.Clone();
            return v;
        }

        /// <summary> Adds a variable to the container.
        /// 
        /// </summary>
        /// <param name="v">the variable to add.
        /// </param>
        public virtual void addVariable(BaseType v)
        {
            vals = v.NewPrimitiveVector();
            Name = v.Name;
            v.Parent = this;
        }


        /// <summary> Write the variable's declaration in a C-style syntax. This
        /// function is used to create textual representation of the Data
        /// Descriptor Structure (DDS).  See <em>The OPeNDAP User Manual</em> for
        /// information about this structure.
        /// 
        /// </summary>
        /// <param name="os">        The <code>StreamWriter</code> on which to Print the
        /// declaration.
        /// </param>
        /// <param name="space">     Each line of the declaration will begin with the
        /// characters in this string.  Usually used for leading spaces.
        /// </param>
        /// <param name="Print_semi">a bool value indicating whether to Print a
        /// semicolon at the end of the declaration.
        /// </param>
        /// <seealso cref="BaseType.PrintDeclaration(StreamWriter,string,bool)">
        /// </seealso>
        public override void PrintDeclaration(StreamWriter os, String space, bool print_semi, bool constrained)
        {
            // BEWARE! Since PrintDeclaration()is (multiple) overloaded in BaseType
            // and all of the different signatures of PrintDeclaration() in BaseType
            // lead to one signature, we must be careful to override that
            // SAME signature here. That way all calls to PrintDeclaration() for
            // this object lead to this implementation.

            //os.println("DVector.PrintDeclaration()");
            os.Write(space + TypeName);
            vals.printDecl(os, " ", print_semi, constrained);
        }

        /// <summary> Prints the value of the variable, with its declaration.  This
        /// function is primarily intended for debugging OPeNDAP applications and
        /// text-based clients such as geturl.
        /// 
        /// </summary>
        /// <param name="os">          the <code>StreamWriter</code> on which to Print the value.
        /// </param>
        /// <param name="space">       this value is passed to the <code>PrintDeclaration</code> method,
        /// and controls the leading spaces of the output.
        /// </param>
        /// <param name="Print_decl_p">a bool value controlling whether the
        /// variable declaration is printed as well as the value.
        /// </param>
        /// <seealso cref="BaseType.PrintValue(StreamWriter,string,bool)">
        /// </seealso>
        public override void PrintValue(StreamWriter os, String space, bool print_decl_p)
        {
            if (print_decl_p)
            {
                PrintDeclaration(os, space, false);
                os.Write(" = ");
            }

            os.Write("{ ");
            vals.printVal(os, "");

            if (print_decl_p)
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine("};");
            }
            else
                os.Write("}");
        }

        /// <summary> Reads data from a <code>DataInputStream</code>. This method is only used
        /// on the client side of the OPeNDAP client/server connection.
        /// 
        /// </summary>
        /// <param name="source">  a <code>DataInputStream</code> to Read from.
        /// </param>
        /// <param name="sv">      the <code>ServerVersion</code> returned by the server.
        /// </param>
        /// <param name="statusUI">the <code>StatusUI</code> object to use for GUI updates
        /// and user cancellation notification (may be null).
        /// </param>
        /// <throws>  EOFException      if EOF is found before the variable is completely </throws>
        /// <summary>                           deserialized.
        /// </summary>
        /// <throws>  IOException       thrown on any other InputStream exception. </throws>
        /// <throws>  DataReadException if an unexpected value was Read. </throws>
        /// <seealso cref="IClientIO.Deserialize">
        /// </seealso>
        //UPGRADE_TODO: Class 'java.io.DataInputStream' was converted to 'System.IO.BinaryReader' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioDataInputStream'"
        //UPGRADE_NOTE: Synchronized keyword was removed from method 'Deserialize'. Lock expression was added. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1027'"
        public virtual void Deserialize(BinaryReader source, ServerVersion sv, IStatusUI statusUI)
        {
            lock (this)
            {
                // Because arrays of primitive types (ie int32, float32, byte, etc) are
                // handled in the C++ core using the XDR package we must Read the
                // length twice for those types. For BaseType vectors, we should Read
                // it only once. This is in effect a work around for a bug in the C++
                // core as the C++ core does not consume 2 length values for the
                // BaseType vectors. Bummer...

                int length = source.ReadInt32();

                if (!(vals is BaseTypePrimitiveVector))
                {
                    // because both XDR and OPeNDAP write the length, we must Read it twice
                    int length2 = source.ReadInt32();
                    //System.out.println("array1 length Read: "+getName()+" "+length+ " -- "+length2);
                    //System.out.println("  array type = : "+vals.getClass().getName());

                    // QC the second length
                    if (length != length2)
                    {
                        throw new DataReadException("Inconsistent array length Read: " + length + " != " + length2);
                    }
                }
                    /* else {
				System.out.println("array2 length Read: "+getName()+" "+length);
				System.out.println("  array type = : "+vals.getClass().getName());
				} */

                if (length < 0)
                    throw new DataReadException("Negative array length Read.");
                if (statusUI != null)
                    statusUI.IncrementByteCount(8);
                vals.Length = length;
                vals.Deserialize(source, sv, statusUI);
            }
        }

        /// <summary> Writes data to a <code>DataOutputStream</code>. This method is used
        /// primarily by GUI clients which need to download OPeNDAP data, manipulate
        /// it, and then re-save it as a binary file.
        /// 
        /// </summary>
        /// <param name="sink">a <code>DataOutputStream</code> to write to.
        /// </param>
        /// <throws>  IOException thrown on any <code>OutputStream</code> </throws>
        /// <summary>                     exception.
        /// </summary>
        //UPGRADE_TODO: Class 'java.io.DataOutputStream' was converted to 'System.IO.BinaryWriter' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioDataOutputStream'"
        public virtual void Externalize(BinaryWriter sink)
        {
            // Because arrays of primitive types (ie int32, float32, byte, etc) are
            // handled in the C++ core using the XDR package we must write the
            // length twice for those types. For BaseType vectors, we should write
            // it only once. This is in effect a work around for a bug in the C++
            // core as the C++ core does not consume 2 length values for thge
            // BaseType vectors. Bummer...
            int length = vals.Length;
            sink.Write(length);

            if (!(vals is BaseTypePrimitiveVector))
            {
                // because both XDR and OPeNDAP write the length, we must write it twice
                sink.Write(length);
            }

            vals.Externalize(sink);
        }
    }
}